home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / umich / network / ka9q / ka9q_src.arc / FTPSERV.C < prev    next >
C/C++ Source or Header  |  1988-07-28  |  17KB  |  689 lines

  1. /* FTP Server state machine - see RFC 959 */
  2.  
  3. #define    LINELEN        128    /* Length of command buffer */
  4.  
  5. #include <stdio.h>
  6. #include "global.h"
  7. #include "mbuf.h"
  8. #include "netuser.h"
  9. #include "timer.h"
  10. #include "tcp.h"
  11. #include "ftp.h"
  12.  
  13. /* Command table */
  14. static char *commands[] = {
  15.     "user",
  16. #define    USER_CMD    0
  17.     "acct",
  18. #define    ACCT_CMD    1
  19.     "pass",
  20. #define    PASS_CMD    2
  21.     "type",
  22. #define    TYPE_CMD    3
  23.     "list",
  24. #define    LIST_CMD    4
  25.     "cwd",
  26. #define    CWD_CMD        5
  27.     "dele",
  28. #define    DELE_CMD    6
  29.     "name",
  30. #define    NAME_CMD    7
  31.     "quit",
  32. #define    QUIT_CMD    8
  33.     "retr",
  34. #define    RETR_CMD    9
  35.     "stor",
  36. #define    STOR_CMD    10
  37.     "port",
  38. #define    PORT_CMD    11
  39.     "nlst",
  40. #define    NLST_CMD    12
  41.     "pwd",
  42. #define    PWD_CMD        13
  43.     "xpwd",            /* For compatibility with 4.2BSD */
  44. #define    XPWD_CMD    14
  45.     "mkd ",
  46. #define    MKD_CMD        15
  47.     "xmkd",            /* For compatibility with 4.2BSD */
  48. #define    XMKD_CMD    16
  49.     "xrmd",            /* For compatibility with 4.2BSD */
  50. #define    XRMD_CMD    17
  51.     "rmd ",
  52. #define    RMD_CMD        18
  53.     NULLCHAR
  54. };
  55.  
  56. /* Response messages */
  57. static char banner[] = "220 %s FTP version %s ready at %s\r\n";
  58. static char badcmd[] = "500 Unknown command\r\n";
  59. static char givepass[] = "331 Enter PASS command\r\n";
  60. static char logged[] = "230 Logged in\r\n";
  61. static char typeok[] = "200 Type OK\r\n";
  62. static char deleok[] = "250 File deleted\r\n";
  63. static char mkdok[] = "200 MKD ok\r\n";
  64. static char delefail[] = "550 Delete failed\r\n";
  65. static char pwdmsg[] = "257 \"%s\" is current directory\r\n";
  66. static char badtype[] = "501 Unknown type \"%s\"\r\n";
  67. static char badport[] = "501 Bad port syntax\r\n";
  68. static char unimp[] = "502 Command not yet implemented\r\n";
  69. static char bye[] = "221 Goodbye!\r\n";
  70. static char nodir[] = "553 Can't read directory \"%s\"\r\n";
  71. static char cantopen[] = "550 Can't read file \"%s\"\r\n";
  72. static char sending[] = "150 Opening data connection for %s %s\r\n";
  73. static char cantmake[] = "553 Can't create \"%s\"\r\n";
  74. static char portok[] = "200 Port command okay\r\n";
  75. static char rxok[] = "226 File received OK\r\n";
  76. static char txok[] = "226 File sent OK\r\n";
  77. static char noperm[] = "550 Permission denied\r\n";
  78. static char noconn[] = "425 Data connection refused\r\n";
  79. static char notlog[] = "530 Please log in with USER and PASS\r\n";
  80.  
  81. static struct tcb *ftp_tcb;
  82.  
  83. /* Start up FTP service */
  84. ftp_start(argc,argv)
  85. int argc;
  86. char *argv[];
  87. {
  88.     struct socket lsocket;
  89.     void ftpscr(),ftpscs();
  90.  
  91.     lsocket.address = ip_addr;
  92.     if(argc < 2)
  93.         lsocket.port = FTP_PORT;
  94.     else
  95.         lsocket.port = atoi(argv[1]);
  96.  
  97.     ftp_tcb = open_tcp(&lsocket,NULLSOCK,TCP_SERVER,0,ftpscr,NULLVFP,ftpscs,0,(char *)NULL);
  98. }
  99. ftp_stop()
  100. {
  101.     if(ftp_tcb != NULLTCB)
  102.         close_tcp(ftp_tcb);
  103. }
  104. /* FTP Server Control channel State change upcall handler */
  105. static
  106. void
  107. ftpscs(tcb,old,new)
  108. struct tcb *tcb;
  109. char old,new;
  110. {
  111.     extern char hostname[],version[];
  112.     struct ftp *ftp,*ftp_create();
  113.     void ftp_delete();
  114.     char *inet_ntoa();
  115.     long t;
  116.     char *cp,*cp1;
  117.  
  118.     switch(new){
  119. /* Setting QUICKSTART piggybacks the server's banner on the SYN/ACK segment;
  120.  * leaving it unset waits for the three-way handshake to complete before
  121.  * sending the banner. Piggybacking unfortunately breaks some old TCPs,
  122.  * so its use is not (yet) recommended.
  123. */
  124. #ifdef    QUICKSTART
  125.     case SYN_RECEIVED:
  126. #else
  127.     case ESTABLISHED:
  128. #endif
  129.         if((ftp = ftp_create(LINELEN)) == NULLFTP){
  130.             /* No space, kill connection */
  131.             close_tcp(tcb);
  132.             return;
  133.         }
  134.         ftp->control = tcb;        /* Downward link */
  135.         tcb->user = (char *)ftp;    /* Upward link */
  136.  
  137.         /* Set default data port */
  138.         ftp->port.address = tcb->conn.remote.address;
  139.         ftp->port.port = FTPD_PORT;
  140.  
  141.         /* Note current directory */
  142.         log(tcb,"open FTP");
  143.         time(&t);
  144.         cp = ctime(&t);
  145.         if((cp1 = index(cp,'\n')) != NULLCHAR)
  146.             *cp1 = '\0';
  147.         tprintf(ftp->control,banner,hostname,version,cp);
  148.         break;        
  149.     case CLOSE_WAIT:
  150.         close_tcp(tcb);
  151.         break;
  152.     case CLOSED:
  153.         log(tcb,"close FTP");
  154.         if((ftp = (struct ftp *)tcb->user) != NULLFTP)
  155.             ftp_delete(ftp);
  156.         /* Check if server is being shut down */
  157.         if(tcb == ftp_tcb)
  158.             ftp_tcb = NULLTCB;
  159.         del_tcp(tcb);
  160.         break;
  161.     }
  162. }
  163.  
  164. /* FTP Server Control channel Receiver upcall handler */
  165. static
  166. void
  167. ftpscr(tcb,cnt)
  168. struct tcb *tcb;
  169. int16 cnt;
  170. {
  171.     register struct ftp *ftp;
  172.     char c;
  173.     struct mbuf *bp;
  174.     void ftpcommand();
  175.  
  176.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  177.         /* Unknown connection, just kill it */
  178.         close_tcp(tcb);
  179.         return;
  180.     }
  181.     switch(ftp->state){
  182.     case COMMAND_STATE:
  183.         /* Assemble an input line in the session buffer. Return if incomplete */
  184.         recv_tcp(tcb,&bp,0);
  185.         while(pullup(&bp,&c,1) == 1){
  186.             switch(c){
  187.             case '\r':    /* Strip cr's */
  188.                 continue;
  189.             case '\n':    /* Complete line; process it */
  190.                 ftp->buf[ftp->cnt] = '\0';
  191.                 ftpcommand(ftp);
  192.                 ftp->cnt = 0;
  193.                 break;
  194.             default:    /* Assemble line */
  195.                 if(ftp->cnt != LINELEN-1)
  196.                     ftp->buf[ftp->cnt++] = c;
  197.                 break;
  198.             }
  199.         }
  200.         /* else no linefeed present yet to terminate command */
  201.         break;
  202.     case SENDING_STATE:
  203.     case RECEIVING_STATE:
  204.         /* Leave commands pending on receive queue until
  205.          * present command is done
  206.          */
  207.         break;
  208.     }
  209. }
  210.  
  211. /* FTP server data channel connection state change upcall handler */
  212. void
  213. ftpsds(tcb,old,new)
  214. struct tcb *tcb;
  215. char old,new;
  216. {
  217.     register struct ftp *ftp;
  218.  
  219.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  220.         /* Unknown connection. Kill it */
  221.         del_tcp(tcb);
  222.     } else if((old == FINWAIT1 || old == CLOSING) && ftp->state == SENDING_STATE){
  223.         /* We've received an ack of our FIN while sending; we're done */
  224.         ftp->state = COMMAND_STATE;
  225.         tprintf(ftp->control,txok);
  226.         /* Kick command parser if something is waiting */
  227.         if(ftp->control->rcvcnt != 0)
  228.             ftpscr(ftp->control,ftp->control->rcvcnt);
  229.     } else if(ftp->state == RECEIVING_STATE && new == CLOSE_WAIT){
  230.         /* FIN received on incoming file */
  231. #ifdef    CPM
  232.         if(ftp->type == ASCII_TYPE)
  233.             putc(CTLZ,ftp->fp);
  234. #endif
  235.         close_tcp(tcb);
  236.         if(ftp->fp != stdout)
  237.             fclose(ftp->fp);
  238.         ftp->fp = NULLFILE;
  239.         ftp->state = COMMAND_STATE;
  240.         tprintf(ftp->control,rxok);
  241.         /* Kick command parser if something is waiting */
  242.         if(ftp->control->rcvcnt != 0)
  243.             ftpscr(ftp->control,ftp->control->rcvcnt);
  244.     } else if(new == CLOSED){
  245.         if(old == SYN_SENT){
  246.             /* Data connection was refused, complain about it */
  247.             tprintf(ftp->control,noconn);
  248.             /* And clean up */
  249.             if(ftp->fp != NULLFILE && ftp->fp != stdout)
  250.                 fclose(ftp->fp);
  251.             ftp->fp = NULLFILE;
  252.             ftp->state = COMMAND_STATE;
  253.             /* Kick command parser if something is waiting */
  254.             if(ftp->control->rcvcnt != 0)
  255.                 ftpscr(ftp->control,ftp->control->rcvcnt);
  256.         }
  257.         /* Clear only if another transfer hasn't already started */
  258.         if(ftp->data == tcb)
  259.             ftp->data = NULLTCB;
  260.         del_tcp(tcb);
  261.     }
  262. }
  263.  
  264. /* Parse and execute ftp commands */
  265. static
  266. void
  267. ftpcommand(ftp)
  268. register struct ftp *ftp;
  269. {
  270.     void ftpdr(),ftpdt(),ftpsds();
  271.     char *cmd,*arg,*cp,**cmdp,*file;
  272.     char *pathname();
  273.     char *mode;
  274.     struct socket dport;
  275.     
  276. #ifndef    CPM
  277.     FILE *dir();
  278. #endif
  279.  
  280.     cmd = ftp->buf;
  281.     if(ftp->cnt == 0){
  282.         /* Can't be a legal FTP command */
  283.         tprintf(ftp->control,badcmd);
  284.         return;
  285.     }    
  286.     cmd = ftp->buf;
  287.  
  288. #ifdef    UNIX
  289.     /* Translate first word to lower case */
  290.     for(cp = cmd;*cp != ' ' && *cp != '\0';cp++)
  291.         *cp = tolower(*cp);
  292. #else
  293.     /* Translate entire buffer to lower case */
  294.     for(cp = cmd;*cp != '\0';cp++)
  295.         *cp = tolower(*cp);
  296. #endif
  297.     /* Find command in table; if not present, return syntax error */
  298.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  299.         if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
  300.             break;
  301.     if(*cmdp == NULLCHAR){
  302.         tprintf(ftp->control,badcmd);
  303.         return;
  304.     }
  305.     /* Allow only USER, PASS and QUIT before logging in */
  306.     if(ftp->cd == NULLCHAR || ftp->path == NULLCHAR){
  307.         switch(cmdp-commands){
  308.         case USER_CMD:
  309.         case PASS_CMD:
  310.         case QUIT_CMD:
  311.             break;
  312.         default:
  313.             tprintf(ftp->control,notlog);
  314.             return;
  315.         }
  316.     }
  317.     arg = &cmd[strlen(*cmdp)];
  318.     while(*arg == ' ')
  319.         arg++;
  320.  
  321.     /* Execute specific command */
  322.     switch(cmdp-commands){
  323.     case USER_CMD:
  324.         if((ftp->username = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
  325.             close_tcp(ftp->control);
  326.             break;
  327.         }
  328.         strcpy(ftp->username,arg);
  329.         tprintf(ftp->control,givepass);
  330.         break;
  331.     case TYPE_CMD:
  332.         switch(arg[0]){
  333.         case 'A':
  334.         case 'a':    /* Ascii */
  335.             ftp->type = ASCII_TYPE;
  336.             tprintf(ftp->control,typeok);
  337.             break;
  338.         case 'B':
  339.         case 'b':    /* Binary